#pragma once

#include <vector>
#include <map>
#include "Matrix3D.h"
class CMemDC;
class OurObject;


using namespace std;

class OurModel
{
public:
	typedef pair<pair<OurObject*,unsigned>,vector<Matrix3D*> > Polygon;
	typedef enum{
		FLAT,
		GOURAUD,
		PHONG
	} ShadingMethod;
	OurModel();
	~OurModel(void);
	void clear(void);
	void reset (void);
	
	void paint (CMemDC &memDC,
				bool bDrawBoundingBox = false,
				bool bDrawAxes = false,
				bool bDrawFaceNormals = false,
				bool bDrawVertexNormals = false);
	
	OurObject *createObject ();
	void MultiplyWithMatrix(const Matrix3D& m);
	Matrix3D TranslateCenterOfMassToOrigin();
	double GetCapacity();
	double GetArea();
	void SetColor(int nWireColor);
	bool IsChanged() const ;
	void SetChanged(bool bIsChanged);
	void ZBufferScanLine(CMemDC& memDC,
		int nRectYmin,
		int nRectYmax,
		int nRectXmin,
		int nRectXmax,
		double dbRectZMax,
		double dbRectZMin,
		int nBackgroundR,int nBackgroundG,int nBackgroundB,
		vector<pair<Matrix3D,bool> >& LightSources,
		vector<Matrix3D>& LightSourcesIntensity,
		Matrix3D& AmbientIntensity);

	void BackFaceCulling(bool bIsOn,bool bNormalSwitchDirection);
	void SetShadingMethod(ShadingMethod eMethod);
	ShadingMethod GetShadingMethod();

	double GetAmbientFactor();
	double GetDiffuseFactor();
	double GetSpecularFactor();
	double GetCosineFactor();

	void SetAmbientFactor(double dbAmbient);
	void SetDiffuseFactor(double dbDiffuse);
	void SetSpecularFactor(double dbSpecular);
	void SetCosineFactor(int nCosine);

public:


	ShadingMethod m_ShadingMethod;

	double m_dbSumXcoord;
	double m_dbSumYcoord;
	double m_dbSumZcoord;
	int m_nNumOfPoints;
	double m_dbMaxXcoord;
	double m_dbMaxYcoord;
	double m_dbMaxZcoord;
	double m_dbMinXcoord;
	double m_dbMinYcoord;
	double m_dbMinZcoord;

	class ZBufferComparator
	{
	public:
		bool operator()(const pair<int,int>& p1,const pair<int,int>& p2)
		{
			if(p1.first<p2.first)
				return true;
			if(p1.first>p2.first)
				return false;
			if(p1.second<p2.second)
				return true;
			return false;
		}
	};

	class FacesetYComparator
	{
	public:
		bool operator()(const Polygon& f1,const Polygon& f2)
		{
			return OurModel::MinYInFaceset(f1)<OurModel::MinYInFaceset(f2);
		}
	};
private:
	
	Matrix3D m_ViewVector;
	
	std::vector< vector<Matrix3D> > m_Scene;
	int m_nSceneYmin;
	int m_nSceneXmin;
	int m_nMaxRed;
	int m_nMaxGreen;
	int m_nMaxBlue;

	double m_dbAmbient;
	double m_dbDiffuse;
	double m_dbSpecular;
	int m_nCosineFactor;
	
	std::vector<OurObject*> m_objects;
	bool m_bIsChanged;

	static int MinYInFaceset(const Polygon& face);
	static int MaxYInFaceset(const Polygon& face);
	
	int FindIntersection(int x1,int x2,int y1,int y2,int nY);
	//Matrix3D FindIntersection(const pair<Matrix3D*,Matrix3D*>& edge,int nY);

	/*void ScanLine(Polygon& Face,
				  map<pair<int,int>,pair<double,int>,ZBufferComparator>& ZBuffer,
				  int y);*/
	void ScanLine(
		Polygon& Face,
		vector<pair<double,Matrix3D> >& ZBuffer,
		int y,
		int nScreenXmin,
		vector<pair<Matrix3D,bool> >& LightSources,
		vector<Matrix3D>& LightSourcesIntensity,
		Matrix3D& AmbientIntensity);

  	/*void paint(CMemDC& memDC,
			   map<pair<int,int>,pair<double,int>,ZBufferComparator>& ZBuffer);*/

	void paintRendered(CMemDC& memDC);


	void EmptyZBufferToScene(
		int nYmin,
		int nXmin,
		vector<pair<double,Matrix3D> >& ZBuffer);

	double CalculateValues(pair<Matrix3D*,Matrix3D*>& edge,
		Polygon& p,
		int nFirstVertex,
		int nSecondVertex,
		int x,
		int y,
		Matrix3D& Color,
		Matrix3D& Normal);

	int GetVertexIndex(Polygon& Face,unsigned IndexInPolygon);
	//Matrix3D* GetVertex(Polygon& p,unsigned index)
};


